/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import {toKatexString,toVector} from "../../../common/Vector";
import 'katex/dist/katex.min.css';
import { showMathStringInTable } from "../../../visual/Table2D";
import NumpyJS from 'jsnumpy';
import { mdRender } from "../../../common/Common";



class KNN extends AlgorithmBase{
  constructor(){
    super('KNN');
    this.descriptionUrl = `${this.homepage}/data/slm/ch3/3.1/readme.md`;
    this.datasetUrl = `${this.homepage}/data/slm/ch9/9.1/dataset.json`;
  }
  init(){ 
    this.reset();
    this.initTrainDS();
    this.inputdata();
  }
  
  reset(){
    // this.dataset = undefined;
    // this.trainDS = undefined;
    this.clear();
    this.initDescription();
    this.updateModel();
  }
  
  
  loadModel(json){
    if(json.TrainDS){
      this.trainDSList = [];
      if(json.TrainDS instanceof Array){
        for(let i = 0, il = json.TrainDS.length; i < il; i++){
          const newTrain = json.TrainDS[i];
          newTrain.T = toVector(newTrain.T, true);
          this.trainDSList.push(newTrain);
        }
        this.trainDS = this.trainDSList[0];
      }
      else{
        this.trainDS = json.TrainDS.x;
      }
    } 
  }

  initDescription(){
    fetch(this.descriptionUrl).then((response)=>{
      return response.text();
    }).then((txt)=>{

      this.reandMD(txt, this.descriptionDiv)
    })
  }

  initModelUI(){
    this.addLabel(this.domElementList.model, '\K:');
    this.kInput = this.addFloatInput(
      this.domElementList.model,  this.k,
      (event)=>{
        const val = parseFloat(event.target.value);
        this.k = val;
        this.updateModel();
      });
    this.modelDiv = this.addDiv(this.domElementList.model);
  }
  
  initTrainDS(){
    this.prepare(this.datasetUrl);
    var data = this.creatdataset().group;
    var label = this.creatdataset().labels;
    var table = [];
    this.k = 0;
    table.push([String.raw`\textbf{ID}`,String.raw`\textbf{电影分类}`,String.raw`\textbf{打斗次数}`, String.raw`\textbf{拥抱次数}`])
    for(var i=0;i<data.length;i++){
      table.push([]);
      table[i+1][0] = String.raw`${i}`;
      table[i+1][1] = String.raw`${label[i]}`;
      table[i+1][2] = String.raw`${data[i][0]}`;
      table[i+1][3] = String.raw`${data[i][1]}`;
    }
    if(this.inputDiv) this.inputDiv.innerHTML = '';
    showMathStringInTable(this.inputDiv,table)
  }


  // 准备数据训练数据
  prepare(url){
    fetch(url).then((response)=>{
      return response.json();
    }).then((json)=>{
      this.trainDS = this.loadModel(json);
      this.updateModel();
    })
  }

  openFileSelector(){
    this.fileSelector.click();
  }

  // 获取文件路径加载
  handleFileChange(event){
    const files = event.target.files;

    for(let i = 0, il = files.length; i < il; i++){
      let reader = new FileReader();
      reader.onload = (e) => {      
        this.prepare(e.target.result);
        if(i === files.length - 1){
          event.target.value = '';
        }
      };
      reader.readAsDataURL(files[i])
    }
  }

   // 输入数据UI
   initInputUI(){

    // 文件选择器
    const fileSelector      = document.createElement('input');
    fileSelector.type       = 'file';
    fileSelector.hidden     = true;
    fileSelector.onchange   = this.handleFileChange.bind(this);   
    
    this.addButton(this.domElementList.input, "获取服务器数据", this.initTrainDS.bind(this));
    this.addButton(this.domElementList.input, "使用本地数据", this.openFileSelector.bind(this));
    this.domElementList.input.appendChild(fileSelector);
    this.addButton(this.domElementList.input, "计算", this.train.bind(this));
    this.labelTrainTips = this.addLabel(this.domElementList.input, '');
    this.inputDiv       = this.addDiv(this.domElementList.input);
    this.fileSelector  = fileSelector;
  }


  // 输出UI
  initOutputUI(){
    this.outputDiv   = this.addDiv(this.domElementList.output);
    this.trainLogDiv = this.addDiv(this.domElementList.output);
  }

  creatdataset(){
    var group = [[1,101],[5,89],[108,5],[115,8]];
    var labels = ['爱情片','爱情片','动作片','动作片']
    return {
      group,
      labels
    }
  }
  
  set_fun(matrix){
    var set_matrix = [];
    for(var i=0,il=matrix.length;i<il;i++){
      if(set_matrix.indexOf(matrix[i])===-1){
        set_matrix.push(matrix[i]);
      }
    }
    return set_matrix;
  }//返回无重复元素的列表
  
  sort(arr){
    for(var i=0;i<arr.length;i++){
      for(var j=i+1;j<arr.length;j++){
        if (arr[i]>arr[j]){
          var temp = arr[i];
          arr[i] = arr[j];
          arr[j] = temp;
        }
      }
    }
    return arr;
  }

  index(arr){
    var old_arr = [...arr]
    var arr_sort = this.sort(arr);
    var new_arr = []
    for(var i=0;i<arr.length;i++){
      new_arr.push(old_arr.indexOf(arr_sort[i]))
    }
    return new_arr;
    
  }

  classify(inx,dataset,labels,k){
    k = this.k;
    var datasetsize = dataset.length;
    var diffmat = [];
    for(var i=0;i<datasetsize;i++){
      diffmat.push(inx);
    }
    diffmat = NumpyJS.subtract(diffmat,dataset);
    var sqdiffmat = NumpyJS.square(diffmat);
    var sqdistances = [];
    var sum = 0;
    for(var j=0;j<datasetsize;j++){
      for(var n=0;n<dataset[j].length;n++){
        sum+= sqdiffmat[j][n];
      }
      sqdistances.push(sum);
      sum = 0;
    }
    var distances = NumpyJS.nThRoot(sqdistances,2);
    var sorteddistindices = this.index(distances);
    var sort_label = this.set_fun(labels);
    var classcount = {};
    for(var m=0;m<sort_label.length;m++){
      classcount[sort_label[m]]=0;
    }
    for(var i=0;i<k;i++){
      var votelabel = labels[sorteddistindices[i]]
      classcount[votelabel]+=1;
    }
    var max = classcount[sort_label[0]];
    for(var i=0;i<Object.keys(classcount).length;i++){
      if(max<classcount[sort_label[i]]){
        max = classcount[sort_label[i]];
      }
    }
    var final_classify = '';
    for(var key in classcount){
      if(classcount[key] ===max){
        final_classify = key;
      }
    }
    return final_classify;

  }

  train(){
    var group = this.creatdataset().group;
    var labels = this.creatdataset().labels;
    var answer = this.classify([this.output1,this.output2],group,labels,this.k)
    mdRender(`该影片为：${answer}`,this.outputDiv);
    
  }

  inputdata(){
    this.addLabel(this.domElementList.output, '打斗次数:');
    this.dataInput = this.addFloatInput(
      this.domElementList.output,  this.output1,
      (event)=>{
        const val = parseFloat(event.target.value);
        this.output1 = val;
        this.updateModel();
      });
    this.outputDiv = this.addDiv(this.domElementList.output);
    this.addLabel(this.domElementList.output, '拥抱次数:');
    this.dataInput2 = this.addFloatInput(
      this.domElementList.output,  this.output2,
      (event)=>{
        const val = parseFloat(event.target.value);
        this.output2 = val;
        this.updateModel();
      });
    this.outputDiv = this.addDiv(this.domElementList.output);
  }
  
  updateModel(){


  }

}



export default KNN;